package kq

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"mall/common/cachex"
	"mall/common/globalkey"
	"mall/common/mqueuex"
	"mall/common/utils/pointy"
	enum "mall/service/backend/common/enum/user"
	"mall/service/backend/rmq/internal/svc"
	"mall/service/order/rpc/orderclient"
	"mall/service/product/rpc/productclient"
	"mall/service/promotion/rpc/promotionclient"
	"mall/service/user/rpc/userclient"

	"github.com/dtm-labs/client/dtmcli/dtmimp"
	"github.com/dtm-labs/client/dtmgrpc"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

/**
 * Listening to the payment flow status change notification message queue
 */
type DeferedUnFreezePointCouponMq struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewDeferedUnFreezePointCouponMq(ctx context.Context, svcCtx *svc.ServiceContext) *DeferedUnFreezePointCouponMq {
	return &DeferedUnFreezePointCouponMq{
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *DeferedUnFreezePointCouponMq) Consume(_, val string) error {
	// 解析数据
	var message mqueuex.DeferedUnFreezePointCouponMessage
	if err := json.Unmarshal([]byte(val), &message); err != nil {
		// logx.WithContext(l.ctx).Error("DeferedUnFreezePointCouponMq.Consume Unmarshal err : %v , val : %s", err, val)
		logx.Errorw("DeferedUnFreezePointCouponMq.Consume json.Unmarshal err", logx.Field("detail", err.Error()))
		return err
	}

	// 执行业务..
	if err := l.execService(&message); err != nil {
		// logx.WithContext(l.ctx).Error("DeferedUnFreezePointCouponMq.execService err : %v , val : %s , message: %+v", err, val, message)
		logx.Errorw("DeferedUnFreezePointCouponMq.execService err", logx.Field("detail", err.Error()),
			logx.Field("val", val), logx.Field("message", fmt.Sprintf("%+v", message)))
		return err
	}

	return nil
}

// Verify and update relevant flow data
func (l *DeferedUnFreezePointCouponMq) execService(message *mqueuex.DeferedUnFreezePointCouponMessage) error {
	if message.OrderSn == nil {
		return errors.Wrapf(OrderUpdateTradeStateError, "Failed to get paid order due to invalid param, message: %+v ", message)
	}
	order, err := l.svcCtx.OrderRpc.GetOrderBySn(l.ctx, &orderclient.GetOrderBySnReq{
		Sn:            message.OrderSn,
		WithSku:       pointy.GetPointer(uint32(1)),
		WithPromotion: pointy.GetPointer(uint32(1)),
		WithPayment:   pointy.GetPointer(uint32(1)),
	})
	// logrus.Info(fmt.Sprintf("DeferedUpdateTradeStateMq execService order: %+v", order))
	if err != nil {
		return errors.Wrapf(OrderUpdateTradeStateError, "Failed to get paid order err: %v , message: %+v ", err, message)
	}

	if order.Id == nil || order.OrderType == nil || order.TradeState == nil || order.UserId == nil || order.Gems == nil {
		return errors.Wrapf(OrderUpdateTradeStateError, "Failed to get valid data of paid order: %+v ", order)
	}

	if *order.OrderType != globalkey.OrderTypeVirtual && *order.TradeState == globalkey.OrderTradeStateNotPay {
		//var paymentId *uint64
		//if len(order.Payments) > 0 {
		//	payment := order.Payments[0]
		//	paymentId = payment.Id
		//}
		var stocks []*productclient.ProductVariationStock
		if len(order.Skus) > 0 {
			for _, v := range order.Skus {
				if v.SkuId != nil && v.Quantity != nil && *v.SkuId > 0 && *v.Quantity > 0 {
					stocks = append(stocks, &productclient.ProductVariationStock{
						Id:    *v.SkuId,
						Stock: *v.Quantity,
					})
				}
			}
		}
		var promotionLogs []*promotionclient.PromotionLog
		if len(order.Promotions) > 0 {
			for _, v := range order.Promotions {
				promotionLogs = append(promotionLogs, &promotionclient.PromotionLog{
					ActivityId:       v.ActivityId,
					ActivityRuleId:   v.ActivityRuleId,
					CouponTemplateId: v.CouponTemplateId,
					CouponId:         v.CouponId,
				})
			}
		}

		//取消冻结log日志
		var keys []string
		userKey := l.getUserUsedCouponKey(*order.UserId)
		orderKey := l.getOrderFrozenKey(*order.Id)
		keys = append(keys, userKey, orderKey)

		var couponIds []string
		var couponMap = make(map[string]struct{})
		var logIds []uint64
		var goodsId uint64

		//收集本次订单用到的couponIds
		orderValue, err := l.svcCtx.BizRedis.GetCtx(context.Background(), orderKey)
		if err != nil {
			return err
		}

		cids, _, lids, gid, point := l.parseOrderValue(orderValue)
		if len(strings.TrimSpace(cids)) > 0 {
			for _, id := range strings.Split(cids, ",") {
				if len(strings.TrimSpace(id)) > 0 {
					couponMap[id] = struct{}{}
				}
			}
		}

		//获取该用户名下所有的已冻结的couponIds   并从中去除本次的coupon（取消冻结）
		userRes, err := l.svcCtx.BizRedis.GetCtx(context.Background(), userKey)
		if err != nil {
			return err
		}
		if len(strings.TrimSpace(userRes)) > 0 {
			for _, id := range strings.Split(userRes, ",") {
				if len(strings.TrimSpace(id)) > 0 {
					if _, ok := couponMap[id]; !ok {
						couponIds = append(couponIds, id)
					}
				}
			}
		}

		if len(strings.TrimSpace(lids)) > 0 {
			for _, id := range strings.Split(lids, ",") {
				if len(strings.TrimSpace(id)) > 0 {
					parseUint, err := strconv.ParseUint(id, 10, 64)
					if err != nil {
						return err
					}
					logIds = append(logIds, parseUint)
				}
			}
		}

		if len(strings.TrimSpace(gid)) > 0 {
			goodsId, err = strconv.ParseUint(gid, 10, 64)
			if err != nil {
				return err
			}
		}

		// Update the flow status.
		err = l.updateTradeStateDtmSaga(
			order.Id,
			order.UserId,
			pointy.GetPointer(point),
			order.Sn,
			logIds,
			goodsId,
			promotionLogs,
		)
		if err != nil {
			return errors.Wrapf(OrderUpdateTradeStateError, "update order tradeState failed, err: %v , message: %+v", err, message)
		}

		//删除本次订单的信息  用户优惠券冻结信息
		_, err = l.svcCtx.BizRedis.DelCtx(context.Background(), keys...)
		if err != nil {
			return err
		}

		//如果去除后该用户名下还有冻结的优惠券  重新添加
		if len(couponIds) > 0 {
			err = l.svcCtx.BizRedis.SetCtx(context.Background(), userKey, strings.Join(couponIds, ","))
			if err != nil {
				return err
			}
		}
	}

	return nil

}

func (l *DeferedUnFreezePointCouponMq) updateTradeStateDtmSaga(
	orderId *uint64,
	userId *uint64,
	point *string,
	orderSn *string,
	logIds []uint64,
	goodsId uint64,
	promotionLogs []*promotionclient.PromotionLog,
) error {
	// 获取 OrderRpc BuildTarget
	//orderRpcBusiServer, err := l.svcCtx.Config.OrderRpc.BuildTarget()
	//if err != nil {
	//	return err
	//}

	// 获取 UserRpc BuildTarget
	userRpcBusiServer, err := l.svcCtx.Config.UserRpc.BuildTarget()
	if err != nil {
		return err
	}

	// 获取 ProductRpc BuildTarget
	//productRpcBusiServer, err := l.svcCtx.Config.ProductRpc.BuildTarget()
	//if err != nil {
	//	return err
	//}

	// 获取 PromotionRpc BuildTarget
	promotionRpcBusiServer, err := l.svcCtx.Config.PromotionRpc.BuildTarget()
	if err != nil {
		return err
	}

	// DefaultGrpcServer default url for grpc server.
	defaultGrpcServer := l.svcCtx.Config.DefaultGrpcServer

	// 创建一个gid
	gid := dtmgrpc.MustGenGid(defaultGrpcServer)

	// 创建一个saga协议的事务
	saga := dtmgrpc.NewSagaGrpc(defaultGrpcServer, gid).
		//Add(
		//	orderRpcBusiServer+"/order.Order/UpdateTradeStateRaw",
		//	orderRpcBusiServer+"/order.Order/UpdateTradeStateRevert",
		//	&orderclient.UpdateTradeStateReq{
		//		Id:               orderId,
		//		TradeState:       tradeState,
		//		TradeStateRevert: tradeStateRevert,
		//		PaymentId:        paymentId,
		//	}).
		Add(
			userRpcBusiServer+"/user.User/UpdateUserAssetRaw",
			userRpcBusiServer+"/user.User/UpdateUserAssetRevert",
			&userclient.UpdateUserAssetReq{
				UserId:       userId,
				AssetType:    pointy.GetPointer(enum.UserAssetTypePoint),          // 积分
				ChangeType:   pointy.GetPointer(enum.UserAssetChangeTypeIncrease), // 增加
				ChangeAmount: point,
				SourceType:   pointy.GetPointer(enum.UserAssetSourceTypeCancelAndReturn), // 返还
				SourceId:     orderId,
				SourceDesc:   orderSn,
			}).
		//Add(
		//	productRpcBusiServer+"/product.Product/UpdateStockRaw",
		//	productRpcBusiServer+"/product.Product/UpdateStockRevert",
		//	&productclient.UpdateStockReq{
		//		Data: stocks,
		//	}).
		Add(
			promotionRpcBusiServer+"/promotion.Promotion/UpdatePromotionLogRaw",
			promotionRpcBusiServer+"/promotion.Promotion/UpdatePromotionLogRevert",
			&promotionclient.UpdatePromotionLogReq{
				UserId:       userId,
				OrderId:      orderId,
				Status:       pointy.GetPointer(uint32(globalkey.StatusStarted)),
				StatusRevert: pointy.GetPointer(uint32(globalkey.StatusUsed)),
				Logs:         promotionLogs,
			}).
		Add(
			userRpcBusiServer+"/user.User/UpdateIntegralTransactionLogRaw",
			userRpcBusiServer+"/user.User/UpdateIntegralTransactionLogRevert",
			&userclient.UpdateIntegralTransactionLogReq{
				Ids:     logIds,
				GoodsId: pointy.GetPointer(goodsId),
			})

	// 事务提交
	err = saga.Submit()
	if err != nil {
		logrus.Info(fmt.Sprintf("DeferedUpdateTradeStateMq updateTradeStateDtmSaga err: %+v", err))
	}
	dtmimp.FatalIfError(err)
	if err != nil {
		return err
	}

	return nil
}

func (l *DeferedUnFreezePointCouponMq) parseOrderValue(value string) (string, string, string, string, string) {
	//value = "优惠券id:活动id:logsids:goodsId:point"
	couponIds := strings.Split(value, ":")[0]
	activityIds := strings.Split(value, ":")[1]
	logsIds := strings.Split(value, ":")[2]
	goodsId := strings.Split(value, ":")[3]
	point := strings.Split(value, ":")[4]
	return couponIds, activityIds, logsIds, goodsId, point
}

func (l *DeferedUnFreezePointCouponMq) getUserUsedCouponKey(uid uint64) string {
	return fmt.Sprintf("{%s}:%d", cachex.UserUsedCoupon, uid)
}

func (l *DeferedUnFreezePointCouponMq) getOrderFrozenKey(oid uint64) string {
	return fmt.Sprintf("{%s}:%d", cachex.OrderFrozen, oid)
}
